/**
 * 
 * gestisce allineamento, ordinamento, ridimensionamento, aggiunta e romozione dei panel 
 * in un pantastePanelContainer
 */
package com.comtaste.pantaste.manager
{
	import com.comtaste.pantaste.components.*;
	import com.comtaste.pantaste.events.DashManagerEvent;
	import com.comtaste.pantaste.events.DashPanelEvent;
	import com.comtaste.pantaste.utilities.DashPanelAspectVO;
	import com.ericfeminella.collections.HashMap;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.filters.GlowFilter;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.ui.Keyboard;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.setTimeout;
	
	import mx.controls.Image;
	import mx.core.Application;
	import mx.core.IFlexDisplayObject;
	import mx.core.UIComponent;
	import mx.effects.Move;
	import mx.effects.Parallel;
	import mx.effects.Resize;
	import mx.events.ChildExistenceChangedEvent;
	import mx.events.FlexEvent;
	import mx.events.IndexChangedEvent;
	import mx.events.ResizeEvent;
	import mx.graphics.ImageSnapshot;
	import mx.managers.ISystemManager;
	import mx.managers.PopUpManager;
	
	/**
	 * A DashLayoutManager manages the possible action of resizing, moving and minimizing in a DashPanelContainer object.
	 * <p>
	 * 	A DashLayoutManager is a multiton that mantains a dictionary of pairs &lt;Number:DashLayoutManager&gt; where
	 * 	the key is the id of the DashPanelContainer whose layout is managed by the corresponding DashLayoutManager object.
	 * </p>
	 * 
	 */
	public class DashLayoutManager extends EventDispatcher
	{
		/**
     	*  The managed panels stack.
     	*/
     	[Bindable] public var panelList:Array = new Array();
     	
		/**
     	*  The managed panel blocks stack.
     	*/
     	[Bindable] public var panelBlockList:Array = new Array();
     	
     	/**
     	 * Dictionary of the icon-DashPanel association.
     	 */ 
		public var icons:Dictionary = new Dictionary();
		
		/**
		 * The managed DashPanelContainer.
		 */ 
		public var container:DashPanelContainer;
		
		/**
		 * The DashPanelHandler.
		 */ 
		protected var handler:DashPanelHandler;
		
		/**
		 * The currently selected panel.
		 */ 
		protected var selectedPanel:DashPanel;
		
		/**
		 * 
		 */ 
		protected var candidatePanel:DashPanel;
		
		/**
		 * 
		 */ 
		protected var candidateBlock:DashBlock;
		
		/**
		 * 
		 */ 
		protected var panelPlaceHolder:DashPanelPlaceHolder = new DashPanelPlaceHolder( );
		
		/**
		 * Indicates whether the handler has been moved (true) or not (false).
		 */		
		protected var handlerMoved:Boolean = false;
				
		/**
		 * Indicates whether the handler has been resized (true) or not (false). 
		 */
		protected var handlerResized:Boolean = false;
				
		/**
		 * 
		 */
		private var singleParallel:Parallel = new Parallel( );
				
		/**
		 * 
		 */
		private var multiParallel:Parallel = new Parallel( );
				
		/**
		 * Indicates whether the panel is minimizing (true) or not (false).
		 */
		private var minimizing:Boolean = false;
				
		/**
		 * Indicates whether the ctrl keyboard key is pressed (true) or not (false). 
		 */
		private var ctrlKeyPressed:Boolean = false;
				
		/**
		 * Reference to the PanSwitcher for the container managed
		 */
		private var panSwitcher:PanSwitcher;
				
		/**
		 * 
		 */
		protected var stopChIndex:Boolean = false;
		
		/**
		 * A systemManager reference
		**/
		private var systemManager:ISystemManager;
				
		/**
		 * Mapping of DashPanelContainers to their relative DashLayoutManagers.
		 */
		private static var multiton:HashMap = new HashMap();
		
				
		/**
		 * Returns the DashLayoutManager that manages the Container identified by instanceKey.
		 * @return DashLayoutManager - the instance of DashLayoutManager that manages the Container identified by instanceKey.
		 */
		public static function getManager( instanceKey:* ):DashLayoutManager
		{
			if( multiton.getValue( instanceKey ) == null && instanceKey != null )
			{
				//multiton.put( instanceKey, new DashLayoutManager( Application.application as DashPanelContainer, instanceKey ) );
			}
			return multiton.getValue( instanceKey ) as DashLayoutManager;
		}
				
		/**
		 * Updates the key of the key-value pair stored in <code>this.multiton</code>.
		 * @param layoutManager:DashLayoutManager The DashLayoutManager whose key has to be changed.
		 * @param newKey:* The new value of the key pointing to <code>layoutManager</code>
		 * @throws Error if the DashLayoutManager has not been registered in the <code>multiton</code> dictionary. 
		 * @see #multiton 
		 */
		public static function updateInstanceKey( layoutManager:DashLayoutManager, newKey:* ):void
		{
			var actualKey:* = multiton.getKey( layoutManager );
			if( actualKey == null )
				throw new Error( "Impossible update a key reference for a not registered DashLayoutManager" );
			// add new key
			multiton.put( newKey, layoutManager );
			// remove old temporary key
			multiton.remove( actualKey );
		}
				
		/**
		 * Gets the details of the DashPanel of the session of the container identified by <code>instanceKey</code>
		 * @param instanceKey:* Key of the instance.
		 * @return Array array of DashPanelAspectVO.
		 * 
		 */
		public static function getManagerPanelsAspectDetails( instanceKey:* ):Array
		{
			var panelDetails:Array;
			var lm:DashLayoutManager = DashLayoutManager.getManager( instanceKey );
			if( lm != null )
			{
				panelDetails = new Array();
				
				var detail:DashPanelAspectVO
				var counter:int = -1;
				var tPanel:DashPanel;
				for each( tPanel in lm.panelList )
				{
					counter++;
					
					detail = new DashPanelAspectVO();
					//detail.id = tPanel.id;
					detail.name = tPanel.name;
					detail.uid = tPanel.uid; // impostare il valore corretto come UID
					
					detail.dashOrder = counter;
					detail.status = tPanel.status;
					detail.alwaysInFront = tPanel.alwaysInFront;
					
					if ( tPanel.status != DashPanel.MINIMIZED )
					{
						detail.restoredX = tPanel.x;
						detail.restoredY = tPanel.y;
						detail.restoredWidth = tPanel.width;
						detail.restoredHeight = tPanel.height;						
					}
					else
					{
						detail.restoredX = tPanel.restoredX;
						detail.restoredY = tPanel.restoredY;
						detail.restoredWidth = tPanel.restoredWidth;
						detail.restoredHeight = tPanel.restoredHeight;	
					}

					detail.visible = tPanel.visible;
					
					// add to panels  aspect list
					panelDetails.push( detail );
				}
			}
			return panelDetails;
		}
		
		/**
     	* Constructor.
     	* <p>
     	* </p>
     	* @param container:DashPanelContainer The container to manage
     	 * @param instanceKey:* The key associated to this container.
     	*/
		public function DashLayoutManager( container:DashPanelContainer, instanceKey:* )
		{
			
			
			this.container = container;
			container.addEventListener( FlexEvent.CREATION_COMPLETE, onContainerComplete )
			container.addEventListener( ChildExistenceChangedEvent.CHILD_ADD, onAddedOnStage );
			container.addEventListener( ResizeEvent.RESIZE, onContainerResize );
			container.addEventListener( FlexEvent.SHOW, onContainerResize );
			container.addEventListener( IndexChangedEvent.CHILD_INDEX_CHANGE, onChildIndexChange );
			/* container.horizontalScrollPolicy = ScrollPolicy.OFF;
			container.verticalScrollPolicy = ScrollPolicy.OFF; */
			multiton.put( instanceKey, this );
			
			systemManager = ( Application.application as Application).systemManager;

			systemManager.addEventListener( KeyboardEvent.KEY_DOWN, onKeyDown,   true );
			systemManager.addEventListener( KeyboardEvent.KEY_UP,   onKeyUp,     true );
			//systemManager.addEventListener( FocusEvent.FOCUS_OUT,   onmouseee,     true );
			
		}
		
				
		
		
		//--------------------------------------
		//  Public Methods
		//--------------------------------------
		
		/**
		 *  Cascades all managed panel from top left to bottom right 
		 * 
		 */	
		public function cascade():void
		{
			if ( container.snapped || container.dashed )
				return;
			
			var openPanelList:Array = getOpenedPanelList();
			
			for(var i:int = 0; i < openPanelList.length; i++)
			{
				var panel:DashPanel = openPanelList[i] as DashPanel;
				
				if ( panel.parent is DashBlock ) continue;
					panel.x = 0;
					panel.y = 0;
				
				panel.x += container.cascadeSize * i;
				panel.y += container.cascadeSize * i;
				
				bringToFront( panel );
				setIconPostion( panel );
			}
		}
		
		/**
		 *  Tiles the window across the screen
		 *  
		 *  <p>By default, windows will be tiled to all the same size and use only the space they can accomodate.
		 *  If you set fillAvailableSpace = true, tile will use all the space available to tile the windows with
		 *  the windows being arranged by varying heights and widths. 
     	 *  </p>
		 * 
		 *  @param fillAvailableSpace:Boolean Variable to determine whether to use the fill the entire available screen
		 * 
		 */
		public function tile( pad:Number = 10 ):void
		{
			if ( container.snapped )
				return;
			
			var openPanelList:Array = getOpenedPanelList();
			var numPanels:int = openPanelList.length;
			
			if(numPanels >= 1)
			{
				var sqrt:int = Math.round(Math.sqrt(numPanels));
				var numCols:int = Math.ceil(numPanels / sqrt);
				var numRows:int = Math.ceil(numPanels / numCols);
				var col:int = 0;
				var row:int = 0;
				var availWidth:Number = this.container.width;
				var availHeight:Number = this.container.height - 30;
				
				var targetWidth:Number = availWidth / numCols - ((pad * (numCols + 1)) / numCols);
				var targetHeight:Number = availHeight / numRows - ((pad * (numRows + 1)) / numRows);
				
				var effectItems:Array = [];
					
				for(var i:int = 0; i < openPanelList.length; i++)
				{
					var win:DashPanel = openPanelList[i];
					
					if ( win.status == DashPanel.MAXIMIZED )
					{
						win.x = 0;
						win.y = 0;
						win.width = container.width;
						win.height = container.height;
						return;
					}
					
					/* win.width = targetWidth;
					win.height = targetHeight; */

					if(i % numCols == 0 && i > 0)
					{
						row++;
						col = 0;
					}
					else if(i > 0)
					{
						col++;
					}
					
					var targetX:int;
					var targetY:int;
					
					targetX = col * targetWidth + pad
					targetY = row * targetHeight + pad 
			
					//pushing out by pad
					if(col > 0) 
						targetX += pad * col;
					
					if(row > 0)
						targetY += pad * row;
						
					setIconPostion( win );
					
					var effectItem:Object = new Object( );
					effectItem["panel"] = win;
					effectItem["widthTo"] = targetWidth;
					effectItem["heightTo"] = targetHeight;
					effectItem["xTo"] = targetX;
					effectItem["yTo"] = targetY;
					effectItems.push( effectItem );
					
				}
				
				applyEffect( win, targetX, targetY, targetWidth, targetHeight, effectItems );
				
/* 				for(var i:int = 0; i < openPanelList.length; i++)
				{
					var win:DashPanel = openPanelList[i] as DashPanel;
					if ( win.status == DashPanel.MAXIMIZED )
					{
						var maxEvent:DashPanelEvent = new DashPanelEvent( DashPanelEvent.MAXIMIZE, win );
						win.dispatchEvent( maxEvent );
						return;
					}
				} */
			}
		}
		
		/**
		 * Returns the list of currently open panels.
		 * 
		 * @return Array list of the open panels
		 */
		public function getOpenedPanelList():Array
		{	
			var array:Array = [];
			for(var i:int = 0; i < panelList.length; i++)
			{
				if( DashPanel(panelList[i]).status != DashPanel.MINIMIZED && DashPanel( panelList[i] ).visible )
				{
					array.push(panelList[i]);
				}
			}
			return array;
		}
		
		/**
		 * Returns a list of open panels that always stay in front
		 * 
		 * @return Array list of open panels that always stay in front
		 */
		public function getAlwaysInFrontPanelList():Array
		{	
			var array:Array = [];
			for(var i:int = 0; i < panelList.length; i++)
			{
				if( DashPanel(panelList[i]).alwaysInFront )
				{
					array.push(panelList[i]);
				}
			}
			return array;
		}
				
		/**
		 * Brings to front a component.
		 * @param element:UIComponent The component to be brought to front.
		 */
		public function bringToFront( element:UIComponent ) : void
		{
			if ( element && container.contains( element ) )
			{
				try {
					container.setChildIndex( element, container.numChildren - 1 );
					if ( element is DashPanel )
					{
						if ( icons[ element ] )
							bringToFront( icons[ element ] );
					}
				} 
				catch ( e:Error ) 
				{
					trace("panel ' '" + element.id + " is not direct children. Cannot bring to front!");
				}
			}
		}
		
		//--------------------------------------
		//  Protected Methods
		//--------------------------------------
				
		/**
		 * Handler of the completion of creation of the container.
		 * @param event:FlexEvent the event of completed creation.
		 * 
		 */
		protected function onContainerComplete( event:FlexEvent ) : void
		{
			if ( container.dashed )
				tile( );
			else
				cascade( );
		}
				
		/**
		 * Handler of the addition of the container to the stage.
		 * @param event:ChildExistenceChangedEvent the event of changed existence of the container
		 */
		protected function onAddedOnStage( event:ChildExistenceChangedEvent ) : void
		{
			var panel:DashPanel;
			
			if ( event.relatedObject is DashBlock )
			{
				var currentBlock:DashBlock = event.relatedObject as DashBlock
				currentBlock.addEventListener( ChildExistenceChangedEvent.CHILD_ADD, onAddedOnStage );
				
				this.panelBlockList.push( currentBlock );
			}
			
			if ( ! ( event.relatedObject is DashPanel ) ) return;
			
			panel = event.relatedObject as DashPanel; 
			
			if( panelList.indexOf( panel ) < 0 )
			{
				this.panelList.push( panel );
				configurePanel( panel );
			}
			
			addHandler();
		}
				
		/**
		 * Positions the icon over the DashPanel passed as argument.
		 * @param panel:DashPanel The panel whose icon has to be positioned
		 */
		protected function setIconPostion( panel:DashPanel ) : void
		{
			if ( icons[ panel ] == null ) return;
			
			var icona:Image = icons[ panel ];
				
				icona.x = panel.x + panel.iconXOffset;
				icona.y = panel.y + panel.iconYOffset;
			
			panel.titleXOffset = icona.contentWidth;
			
			bringToFront( icona );
		}
				
		/**
		 * Adds the DashPanelHandler.
		 * @see com.comtaste.pantaste.components.DashLayoutHandler
		 */
		protected function addHandler( ) : void
		{
			if ( !handler )
			{
				handler = new DashPanelHandler();
				handler.visible = false;
				container.addChild( handler );
				
				handler.addEventListener( DashManagerEvent.PANEL_HANDLER_MOVING, applyConstraintOnMove );
				handler.addEventListener( DashManagerEvent.PANEL_HANDLER_RESIZING, applyConstraintOnResize );
				handler.addEventListener( MouseEvent.MOUSE_OVER, activateHandler );
				/* handler.addEventListener( MouseEvent.MOUSE_OUT, destroyHandler ); */
				handler.addEventListener( MouseEvent.MOUSE_UP, destroyHandler );
				bringToFront( handler );
			}
			
		}
				
		/**
		 * Configures the event listeners and the icon for the DashPanel passed as argument,
		 * @param panel:DashPanel the DashPanel to be configured
		 */
		protected function configurePanel( panel:DashPanel ) : void
		{
			if ( panel.icon )
			{
				createIcon( panel );
			}
			
			if ( panel.parent is DashBlock )
			{
				panel.maximizable = false;
				panel.minimizable = false;
				panel.collapsible = true;
			}
			
			panel.addEventListener( "iconChanged", onIcon );
			
			panel.addEventListener( DashPanelEvent.PANEL_TITLE_OVER, prepareHandler );
			panel.addEventListener( DashPanelEvent.PANEL_RESIZER_OVER, prepareHandler );
			
			panel.addEventListener( FlexEvent.SHOW, onPanelShowHide );
			panel.addEventListener( FlexEvent.HIDE, onPanelShowHide );
			
			panel.addEventListener( DashPanelEvent.MAXIMIZE, onMaximize );
			panel.addEventListener( DashPanelEvent.MINIMIZE, onMinimize );
			panel.addEventListener( DashPanelEvent.RESTORE, onRestore );
			panel.addEventListener( DashPanelEvent.CLOSE, onClose );
			
			panel.addEventListener( MouseEvent.CLICK, onFocusPanel );
		}
				
		/**
		 * Handles the change of the icon over the target panel
		 * @param event:Event the event of type <code>iconChanged</code>
		 */
		private function onIcon( event:Event ) : void
		{
			var panel:DashPanel = event.target as DashPanel;
			
			if ( icons[ panel ] )
			{
				container.removeChild( icons[ panel ] )
				delete icons[ panel ];
			}
			
			if ( panel.icon )
			{
				createIcon( panel );
			}
		}
				
		/**
		 * Creates and brings to front the icon for the <code>panel</code> object.
		 * @param panel:DashPanel the DashPanel whose icon has to be set
		 */
		protected function createIcon( panel:DashPanel ) : void
		{
			var icona:Image = new Image();
				icona.source = panel.icon;
				icona.mouseEnabled = false;
				icona.mouseChildren = false;
				container.addChild( icona );
				icons[ panel ] = icona;
				icona.visible = panel.visible;
				setIconPostion( panel );
				bringToFront( icona );
		}
				
		/**
		 * Handler of the event of showing/hiding a target panel.
		 * @param event:FlexEvent The event of showing/hiding a panel.
		 */
		protected function onPanelShowHide( event:FlexEvent ) : void
		{
			var panel:DashPanel = event.target as DashPanel;
			
			if ( icons[ panel ] != null && !minimizing )
				icons[ panel ].visible = panel.visible;
			
			if ( container.dashed && !minimizing )
				tile( );
				
			minimizing = false;
		}
				
		/**
		 * Executed whenever a target DashPanel gains focus.
		 * @param event:MouseEvent The MouseEvent over the target DashPanel  
		 */
		protected function onFocusPanel( event:MouseEvent ) : void
		{
			if ( event.currentTarget is DashPanel )
			{
				bringToFront( event.currentTarget as DashPanel );
				/* bringToFront( handler ); */
			}
		}
		
		/**
     	*  @private
     	*  Apply constraint after a dashPanelEvent to respect panel parameters
     	*/
		protected function applyConstraint( moved:Boolean = false, resized:Boolean = false ):void
		{	
			if ( container.dashed && !handler.resizer )
			{
				if ( candidatePanel == null ) return;
				
				candidatePanel.filters = [];
	
				var selectedPanelIndex:int = panelList.indexOf( selectedPanel );
				var candidatePanelIndex:int = panelList.indexOf( candidatePanel );
				
				panelList.splice( selectedPanelIndex, 1 );
				
				var firstPanels:Array = panelList.slice(0, candidatePanelIndex);
				var lastPanels:Array = panelList.slice(candidatePanelIndex);
				
				firstPanels.push( selectedPanel );
				panelList = firstPanels.concat( lastPanels );
				
				tile();
				
				candidatePanel = null;
			}
			else if ( container.snapped )
			{
				if ( moved )
				{
					selectedPanel.y = Math.round(handler.y / container.snapSize) * container.snapSize;
					selectedPanel.x = Math.round(handler.x / container.snapSize) * container.snapSize;
				}
				else if ( resized )
				{
					var desDiffHeight:Number = (Math.round(handler.y / container.snapSize) * container.snapSize) - handler.y;
					selectedPanel.height = (Math.round(handler.height / container.snapSize) * container.snapSize) + desDiffHeight;
					
					var desDiffWidth:Number = (Math.round(handler.x / container.snapSize) * container.snapSize) - handler.x;
					selectedPanel.width = (Math.round(handler.width / container.snapSize) * container.snapSize) + desDiffWidth;
				}
			}
			else
			{
				if ( panelPlaceHolder && panelPlaceHolder.parent )
				{
					panelPlaceHolder.parent.addChildAt( selectedPanel, panelPlaceHolder.parent.getChildIndex( panelPlaceHolder ) );
					panelPlaceHolder.parent.removeChild( panelPlaceHolder );
					selectedPanel.visible = true;
					selectedPanel.includeInLayout = true;
					
				}
				else if ( !( selectedPanel.parent is DashBlock ) )
				{
					bringToFront( selectedPanel );
					selectedPanel.x = handler.x;
					selectedPanel.y = handler.y;
					if ( handler.resizer )
					{
						selectedPanel.width = handler.width;
						selectedPanel.height = handler.height;
					}
				}
			}
			
			setIconPostion( selectedPanel );
			
			// generate dash container event
			this.container.generatepantasteChangeEvent( DashPanelContainer.PANEL_MOVED, selectedPanel );
		}
		
		/**
     	*  @private
     	*  Apply constraint after a dashPanelEvent to respect panel parameters
     	*/
		protected function applyConstraintOnMove( event:DashManagerEvent ):void
		{
			var openPanelList:Array = getOpenedPanelList();
			
			if ( container.dashed )
			{
				candidatePanel = null;
				
				for(var i:int = 0; i < openPanelList.length; i++)
				{
					if ( ( event.handler as DashPanelHandler ).hitTestPoint( 
						 	container.x + ( openPanelList[i] as DashPanel ).x + (openPanelList[i] as DashPanel).width / 2,
						 	container.y + ( openPanelList[i] as DashPanel ).y + (openPanelList[i] as DashPanel).height / 2 ) )
					{
						var glowEffect:GlowFilter = new GlowFilter();
							glowEffect.color = 0xAAAAAA;
							glowEffect.alpha = 1;
							glowEffect.blurX= 25;
							glowEffect.blurY = 25;
							
							( openPanelList[i] as DashPanel ).filters = [ glowEffect ];
							
						this.candidatePanel = ( openPanelList[i] as DashPanel );
					}
					else
					{
						( openPanelList[i] as DashPanel ).filters = [ ];
					}
				}
			}
			else if (  selectedPanel.parent is DashBlock || panelPlaceHolder.parent is DashBlock  ) 
			{
				panelPlaceHolder.width = selectedPanel.width;
				panelPlaceHolder.height = selectedPanel.height;
				
				if ( selectedPanel.parent )
				{
					selectedPanel.parent.addChildAt( panelPlaceHolder, selectedPanel.parent.getChildIndex( selectedPanel ) );
					selectedPanel.parent.removeChild( selectedPanel );
					selectedPanel.visible = false;
					selectedPanel.includeInLayout = false;
				}
				
				var candidateItem:Object = hitPanelOrBlock( );

				if ( candidateItem && candidateItem is DashPanel && candidateItem.parent && candidateItem.parent is DashBlock )
				{
					var candidateBlock:DashBlock;
						candidateBlock = candidateItem.parent as DashBlock;
						
					candidateBlock.addChildAt( panelPlaceHolder, candidateBlock.getChildIndex( candidateItem as DashPanel ) );
				}
				else if ( candidateItem && candidateItem is DashBlock )
				{
					( candidateItem as DashBlock ).addChild( panelPlaceHolder );
				}
			}
		}
		
		protected function hitPanelOrBlock( items:Array = null ) : Object
		{
			var handlerGlobalPoints:Point = getGlobalPoints( handler );
				
			var handlerRectangle:Rectangle = new Rectangle( handlerGlobalPoints.x,
															handlerGlobalPoints.y,
															handler.width,
															handler.height );
				
			var distance:Number = 0;
			var nearestItemIndex:int = 0;
			
			items = new Array();
			items = items.concat( getOpenedPanelList() );
			items.push( panelPlaceHolder );
			
			for( var currentItemIndex:int = 0; currentItemIndex < items.length; currentItemIndex++ )
			{
				var item:Object = items[ currentItemIndex ];
				
				var itemGlobalPoints:Point = getGlobalPoints( item );
				
				var itemRectangle:Rectangle = new Rectangle( itemGlobalPoints.x,
															 itemGlobalPoints.y,
															 item.width,
															 item.height );
				
				var intersection:Rectangle = handlerRectangle.intersection( itemRectangle );
				
				var tmpDistance:Number = Point.distance( intersection.bottomRight, intersection.topLeft );
				
				if ( tmpDistance > 0 && distance < tmpDistance  )
				{
					distance = tmpDistance;
					nearestItemIndex = currentItemIndex;
				}
			}
			
			if ( distance > 0 )
				return items[ nearestItemIndex ];
			else
			{
				for( var currentItemIndex:int = 0; currentItemIndex < panelBlockList.length; currentItemIndex++ )
				{
					var item:Object = panelBlockList[ currentItemIndex ];
					
					var itemGlobalPoints:Point = getGlobalPoints( item );
					
					var itemRectangle:Rectangle = new Rectangle( itemGlobalPoints.x,
						itemGlobalPoints.y,
						item.width,
						item.height );
					
					var intersection:Rectangle = handlerRectangle.intersection( itemRectangle );
					
					var tmpDistance:Number = Point.distance( intersection.bottomRight, intersection.topLeft );
					
					if ( tmpDistance > 0 && distance < tmpDistance  )
					{
						distance = tmpDistance;
						nearestItemIndex = currentItemIndex;
					}
				}
				
			}
			
			return panelBlockList[ nearestItemIndex ];
		}
		
		private function getGlobalPoints( item:Object, curPoint:Point = null ) : Point
		{
			if ( curPoint == null )
				curPoint = new Point();
				
			curPoint.x += item.x;
			curPoint.y += item.y;
			
			if ( item.parent )
			{
				return getGlobalPoints( item.parent, curPoint );
			}
			
			return curPoint;
			
		}
		/**
     	*  @private
     	*  Apply constraint after a dashPanel resize Event to respect panel parameters
     	*/
		protected function applyConstraintOnResize( event:DashManagerEvent ):void
		{
		/*	var dh:DashPanelHandler = event.handler;
			dh.width = 100;//dh.width<dh.minimumWidth?dh.minimumWidth:dh.width;
			dh.height = 100;//dh.height<dh.minimumHeight?dh.minimumHeight:dh.height;
			*/
		}
				
		/**
		 * Plays the specified effects over the specified items.
		 * @param panel:DashPanel The DashPanel to which apply the parallel effect.
		 * @param xTo:Number The ending x coordinate for the move effect.
		 * @param yTo:Number The ending y coordinate for the move effect.
		 * @param widthTo:Number The ending width of the resize effect.
		 * @param heightTo:Number The ending height of the resize effect.
		 * @param effectItems:Array The set of effects that must be played on the panel
		 */
		public function applyEffect( panel:DashPanel, xTo:Number, yTo:Number, widthTo:Number, heightTo:Number, effectItems:Array = null ) : void
		{
			if ( effectItems )
			{
				multiParallel.stop();
				multiParallel = new Parallel( );
				for ( var i:int = 0; i < effectItems.length; i++ )
				{
					constructEffect( effectItems[i]["panel"], effectItems[i]["xTo"], effectItems[i]["yTo"], effectItems[i]["widthTo"], effectItems[i]["heightTo"], multiParallel )
					multiParallel.play();
				}
			}
			else
			{
				singleParallel = new Parallel( );
				constructEffect( panel, xTo, yTo, widthTo, heightTo, singleParallel )
				singleParallel.play();
			}
		}
		
		
		
		//--------------------------------------
		//  Private Methods
		//--------------------------------------
				
		/**
		 * Builds the composite parallel effect of resizing and moving of the DashPanel passed as argument.
		 * @param panel:DashPanel The DashPanel to which apply the parallel effect.
		 * @param xTo:Number The ending x coordinate for the move effect.
		 * @param yTo:Number The ending y coordinate for the move effect.
		 * @param widthTo:Number The ending width of the resize effect.
		 * @param heightTo:Number The ending height of the resize effect.
		 */
		private function constructEffect( panel:DashPanel, xTo:Number, yTo:Number, widthTo:Number, heightTo:Number, parallel:Parallel ) : void
		{
			if ( icons[ panel ] != null )
			{
				var iconMoveEffect:Move = new Move( icons[ panel ] );
					iconMoveEffect.xFrom = icons[ panel ].x;
					iconMoveEffect.yFrom  = icons[ panel ].y;
					iconMoveEffect.xTo = xTo + panel.iconXOffset;
					iconMoveEffect.yTo = yTo + panel.iconYOffset;
					parallel.addChild( iconMoveEffect );
			}
			
			var moveEffect:Move = new Move( panel );
				moveEffect.xFrom = panel.x;
				moveEffect.yFrom = panel.y;
				moveEffect.xTo = xTo;
				moveEffect.yTo = yTo;
				
			var resizeEffect:Resize = new Resize( panel );
				resizeEffect.widthFrom = panel.width;
				resizeEffect.heightFrom = panel.height;
				resizeEffect.widthTo = widthTo;
				resizeEffect.heightTo = heightTo;
				
				parallel.addChild( moveEffect );
				parallel.addChild( resizeEffect );
				parallel.duration = 500;
		}
		
		//--------------------------------------
		//  Listeners
		//--------------------------------------
				
		/**
		 * 
		 */
		protected function onKeyDown( event:KeyboardEvent ) : void
		{
			
		}
				
		/**
		 * Handles the keyboard event that brings up the PanSwitcher
		 * @param event:KeyboardEvent
		 * @see com.comtaste.pantaste.components.PanSwitcher
		 */
		protected function onKeyUp( event:KeyboardEvent ) : void
		{
			if ( event.ctrlKey )
			{
				if ( event.keyCode == Keyboard.TAB )
				{
					event.stopImmediatePropagation();
					if( !ctrlKeyPressed )
					{
						ctrlKeyPressed = true;
						activatePanSwitcher();
					} 
					else
					{
						if( panSwitcher )
							panSwitcher.selectNext();  
					}
				/* } else if( event.keyCode == M_KEY ){

					var pod:PodWindow = getPodWindowByFocusedComponent();

					if( pod != null ){
						if(
							pod.enabled &&
							!pod.isDragging() &&
							!pod.isResizing() &&
							pod.showNormalizeButton	
						){
							normalizePod( pod );
						}
					}
				} */
				} 
				
			}
			else if ( event.keyCode == Keyboard.CONTROL )
			{
				ctrlKeyPressed = false;
				deactivatePanSwitcher();
			}
		}
				
		/**
		 * Activates the PanSwitcher.
		 * @see com.comtaste.pantaste.components.PanSwitcher
		 */
		protected function activatePanSwitcher( ) : void
		{
			Application.application.setStyle( "modalTransparency", 0 );
			if ( !panSwitcher )
			{
				panSwitcher = PopUpManager.createPopUp
				(
					DisplayObject( Application.application ),
					PanSwitcher,
					true
				) as PanSwitcher;
			}
			
			for ( var i:int; i < panelList.length; i++ )
			{
				if ( panelList[ i ].visible || ( !panelList[ i ].visible && panelList[ i ].status == DashPanel.MINIMIZED ) )
				{
					if ( icons[ panelList[ i ] ] )
					{
						var imageSnap:ImageSnapshot = ImageSnapshot.captureImage( icons[ panelList[ i ] ] );
			            var imageByteArray:ByteArray = imageSnap.data as ByteArray;
			            
			            panSwitcher.addPanelSnaphshot( imageByteArray, panelList[ i ].title, false );
		   			}
		   			else
		   			{
						var imageSnapOfPanel:ImageSnapshot = ImageSnapshot.captureImage( panelList[ i ] );
			            var imageByteArrayOfPanel:ByteArray = imageSnap.data as ByteArray;
			            
			            panSwitcher.addPanelSnaphshot( imageByteArray, panelList[ i ].title );
		   			}
	   			}
   			}
		}
		
		/**
		 * Deactivates the PanSwitcher.
		 * @see com.comtaste.pantaste.components.PanSwitcher
		**/
		protected function deactivatePanSwitcher():void
		{
			if( panSwitcher )
			{
				PopUpManager.removePopUp( panSwitcher as IFlexDisplayObject );
				Application.application.setStyle( "modalTransparency", 0.5 );
				bringToFront( panelList[ panSwitcher.currentThumb ] );
				if ( icons[ panelList[ panSwitcher.currentThumb ] ] )
					bringToFront( icons[ panelList[ panSwitcher.currentThumb ] ] );
				panSwitcher = null;
			}
		}
		
			
		/**
		 * Handles the DashPanelEvent.
		 * <p>
		 *  It sets the DashPanelHandler visible and on the same coordinates of the DashPanel being moved, or on the bottom-right corner
		 * if being resized.
		 * </p> 
		 * @param event:DashPanelEvent
		 */
		protected function prepareHandler( event:DashPanelEvent ) : void
		{
			var panel:DashPanel = event.panel as DashPanel;
			
			if ( panel.status == DashPanel.MAXIMIZED || ( event.type == DashPanelEvent.PANEL_RESIZER_OVER && container.dashed ) ) return;
			
			handler.visible = true;
			bringToFront( handler );
			selectedPanel = panel;

			if (event.type == DashPanelEvent.PANEL_TITLE_OVER )
			{
				handler.resizer = false;
				handler.width = panel.width;
				handler.height = 35;
				
				var xOffset:Number = 0;
				var yOffset:Number = 0;
				if ( panel.parent is DashBlock )
				{
					xOffset = panel.parent.x;
					yOffset = panel.parent.y;
				}
				
				handler.x = panel.x + xOffset;
				handler.y = panel.y + yOffset;
				handlerMoved = true;
				handlerResized = false;
			}
			else if ( event.type == DashPanelEvent.PANEL_RESIZER_OVER )
			{
				handler.resizer = true;
				handler.width = 10;
				handler.height = 10;
				handler.x = panel.width + panel.x - 10;
				handler.y = panel.height + panel.y - 10;
				handlerMoved = false;
				handlerResized = true;
			}
		}
				
		/**
		 * Hides the current panel's handler.
		 * @param event:MouseEvent The MouseEvent handled
		 */
		protected function destroyHandler( event:MouseEvent ) : void
		{
			if ( event.buttonDown ) return;
			
			handler.resizer = false;
			handler.visible = false;
			handler.setStyle( "backgroundColor", null );
		}
				
		/**
		 * Ends the resizing/replacing routine, resetting the event listeners over the handler
		 * and applying the constraints imposed by the DashPanelContainer properties
		 * @param event:MouseEvent the related MouseEvent.MOUSE_UP type event
		 */
		protected function finishWork( event:MouseEvent ) : void
		{
			if ( event.buttonDown ) return;
			
			applyConstraint( handlerMoved, handlerResized );
			container.stage.removeEventListener( MouseEvent.MOUSE_UP, finishWork );
			destroyHandler( event );
			handler.addEventListener( MouseEvent.MOUSE_OVER, activateHandler );
		}
				
		/**
		 * Executed whenever a user begins to move or resize a panel.
		 * <p>
		 * 	When a move or resize action has been taken, the handler - already visible although with no color - 
		 *  is displayed to give an hint of the effect of the action, therefore appearing like a copy over the 
		 *  DashPanel the user is modifying.
		 * </p>  
		 * @param event:MouseEvent the related MouseEvent.MOUSE_OVER type event
		 */
		protected function activateHandler( event:MouseEvent ) : void
		{
			container.stage.addEventListener( MouseEvent.MOUSE_UP, finishWork );
			handler.removeEventListener( MouseEvent.MOUSE_OVER, activateHandler );
			handler.setStyle( "backgroundColor", 0x000000 );
			
			var imageSnap:ImageSnapshot = ImageSnapshot.captureImage( selectedPanel );
            var imageByteArray:ByteArray = imageSnap.data as ByteArray;
            
			handler.setSnapshot( imageByteArray );
			
			handler.alpha = .3;
			
			var xOffset:Number = 0;
			var yOffset:Number = 0;
			if ( selectedPanel.parent is DashBlock )
			{
				xOffset = selectedPanel.parent.x;
				yOffset = selectedPanel.parent.y;
			}
			
			handler.x = selectedPanel.x + xOffset;
			handler.y = selectedPanel.y + yOffset;
			handler.height = selectedPanel.height;
			handler.width = selectedPanel.width;
			bringToFront( selectedPanel );
			
			if ( icons[ selectedPanel ] )
				bringToFront( icons[ selectedPanel ] );
				
			bringToFront( handler );
		}
				
		/**
		 * Performed every time the container is resized, to relay the open panels.
		 * <p>
		 * If <code>dashed = true</code> then a retiling of the opened DashPanels is performed.
		 * </p>
		 * @param event:Event The resize event.
		 */
		protected function onContainerResize( event:Event ) : void
		{
			var openPanelList:Array = getOpenedPanelList();
			
			for(var i:int = 0; i < openPanelList.length; i++)
			{
				var win:DashPanel = openPanelList[i] as DashPanel;
				if ( win.status == DashPanel.MAXIMIZED )
				{
					var maxEvent:DashPanelEvent = new DashPanelEvent( DashPanelEvent.MAXIMIZE, win );
					win.dispatchEvent( maxEvent );
					return;
				}
			}
			
			if ( container.dashed )
				tile( );
		}
				
		/**
		 * Applies the resize and move effect when the target DashPanel is maximized.
		 * @param event:DashPanelEvent The maximization event.
		 */
		protected function onMaximize( event:DashPanelEvent ) : void
		{
			var panel:DashPanel = event.panel as DashPanel;
			
			if ( panel.status == DashPanel.MAXIMIZED )
				return;
			else if ( panel.status == DashPanel.MINIMIZED )
			{
				panel.visible = true;
				panel.x = panel.restoredX;
				panel.y = panel.restoredY;
				panel.width = panel.restoredWidth;
				panel.height = panel.restoredHeight;
			}
			
			applyEffect( panel, 0, 0, container.width, container.height );
			
			/* panel.x = 0;
			panel.y = 0;
			panel.width = container.width;
			panel.height = container.height; */
			
			bringToFront( panel );
			
			if ( icons[ panel ] )
				bringToFront( icons[ panel ] );
			
			setIconPostion( panel );
			
			panel.status = DashPanel.MAXIMIZED;
			
			// generate dash container event
			this.container.generatepantasteChangeEvent( DashPanelContainer.PANEL_MAXIMIZED, panel );
		}
				
		/**
		 * Applies the resize and move effect when the target DashPanel is minimized.
		 * <p>It furthermore adds it to the DashDock of the current DashPanelContainer.
		 * If <code>dashed = true</code> then a retiling of the opened DashPanels is performed.
		 * </p>
		 * @param event:DashPanelEvent The maximization event.
		 */
		protected function onMinimize( event:DashPanelEvent ) : void
		{
			var panel:DashPanel = event.panel as DashPanel;
			
			if ( panel.status == DashPanel.MINIMIZED )
				return;
			
			minimizing = true;
			
			destroyHandler( new MouseEvent( "dummy" ) );
			
			applyEffect( panel, panel.x, panel.y, 0, 0 );
			
			setTimeout( function():void {
				panel.visible = false;
				if ( icons[ panel ] )
					icons[ panel ].visible = false;
			}, 250);
			container.dock.addPanel( panel );
			
			panel.status = DashPanel.MINIMIZED;
			
			if ( container.dashed )
				tile( );
			
			// generate dash container event
			this.container.generatepantasteChangeEvent( DashPanelContainer.PANEL_MINIMIZED, panel );
		}
				
		/**
		 * Restores the view of the DashPanel that dispatched the event.
		 * <p>
		 * If <code>dashed = true</code> then a retiling of the opened DashPanels is performed.
		 * </p>
		 * @param event:DashPanelEvent The restore event.
		 */
		protected function onRestore( event:DashPanelEvent ) : void
		{
			var panel:DashPanel = event.panel as DashPanel;
			
			if ( panel.status != DashPanel.RESTORED )
			{
				minimizing = true;
				
				panel.visible = true;
				setTimeout( function():void {
					if ( icons[ panel ] )
						icons[ panel ].visible = true;
				}, 250);
				
				bringToFront( panel );
				setIconPostion( panel );
				
				panel.status = DashPanel.RESTORED;
				
				container.dock.removePanel( panel );
				
				if ( container.dashed )
					tile( );
				else
					applyEffect( panel, panel.restoredX, panel.restoredY, panel.restoredWidth, panel.restoredHeight );
				
				// generate dash container event
				this.container.generatepantasteChangeEvent( DashPanelContainer.PANEL_RESTORED, panel );
			}
		}
				
		/**
		 * Executed when a DashPanel closes.
		 * <p>
		 * If <code>dashed = true</code> then a retiling of the opened DashPanels is performed.
		 * </p> 
		 * @param event:DashPanelEvent the DashPanel close event. 
		 */
		protected function onClose( event:DashPanelEvent ) : void
		{
			if ( event.panel.parent is DashBlock )
				event.panel.parent.removeChild( event.panel );
			else
				container.removeChild( event.panel );
			
			event.panel.includeInLayout = false;
			event.panel.visible = false;
			event.panel = null;
			
			if ( icons[ event.panel as DashPanel ] )
				container.removeChild( icons[ event.panel ] );
			
			delete icons[ event.panel ];
			for ( var i:int = 0; i < panelList.length; i++ )
			{
				if ( event.panel == panelList[ i ] )
					panelList.splice( i, 1 );
			}
			
			if ( container.dashed )
				tile( );
		}
		
		/**
		 * Executed when the children order of the container is changed. It rearranges the indexes of the whole childrens' set.
		 * @param event:IndexChangedEvent The index change event
		 */
		protected function onChildIndexChange( event:IndexChangedEvent ) : void
		{
			if ( stopChIndex || event.relatedObject is DashPanelHandler ) return;
			trace("Child index changed");
			var allFrArray:Array = getAlwaysInFrontPanelList();
			
			if ( allFrArray.length > 0 )
			{
				for ( var i:int; i < allFrArray.length; i++ )
				{
					stopChIndex = true;
					bringToFront( allFrArray[ i ] );
					stopChIndex = false;
				}
			}
			
			if ( event.relatedObject is DashPanel && icons[ event.relatedObject ] )
			{
				trace('what now');
				stopChIndex = true;
				var index:int = container.getChildIndex( event.relatedObject );
				//container.setChildIndex( icons[ event.relatedObject ],index  + 1);
				stopChIndex = false;
			}
		}
		
		//--------------------------------------
		//  Setter & Getter
		//--------------------------------------
	}
}